【换根DP】洛谷 P6419 [COCI2014-2015 1] Kamp
2022-03-31 23:03:00
# ACM
题链
https://www.luogu.com.cn/problem/P6419
题解
- 给一棵树 n 个节点,K 个人的家分布在不同节点上,K 个人会在节点 i 处聚会,如果在 i 处聚会,需要送每一个人回去,那么在 i 处聚会最后把 K 个人送回家的最短时间就是 ans[i],求在哪些节点举办聚会 ans 最小,输出最小时间和对应的某些节点。
- 可以分为两部分求 :
- f[u] 表示以 u 节点为聚会点,车载 K 个人送回家并回到 u 节点的最短路程
- mxlen[u] 表示以 u 节点为聚会点,到某个人家的最长路径长度
- 可知 ans[u] = f[u] - mxlen[u];
- 假设以 1 为根,sz[u] 为以 u 为根节点的子树下的人的个数
- 如何求 f[u] :
- 设 g[u] 为以 u 为根的子树,从 u 出发把子树下所有人送回家又回到 u 的最短路程
- g[u] = sum( g[v] + dis(u, v)*2 ); (sz[v] > 0) 得在 v 子树下有人家的情况下更新
- 以上是第一次 dfs
- f[1] = g[1];
- 更新 f[v] 有三种情况 :
- f[v] = g[v]; (sz[v] == K) 也就是所有人都在 v 的子树下
- f[v] = f[u] + dis(u, v)*2; (sz[v] == 0) 也就是所有人都在 v 的父节点 u 的上面(就是不在 v 这棵子树下)
- f[v] = f[u]; (其他) 也就是不仅在 v 的子树下有人家,v 的子树外也有
- 以上是第二次 dfs
- 如何求 mxlen[u] :
- 先将 mxlen[u] 定义为以 u 为根节点的子树,从 u 出发到达子树下某个人家的最长链路
- mxid[u] 定义为以 u 为根节点的子树,从 u 出发到达子树下某个人家的最长链路的第一步走到的孩子节点 v
- cmxlen[u] 定义为以 u 为根节点的子树,从 u 出发到达子树下某个人家的次长链路且链路不与最长链路有交集
- 如何得到该定义下的 mxlen[u], mxid[u], cmxlen[u]:
- mxlen[u] = mxlen[v] + dis(u, v); (sz[v] > 0) 得在 v 子树下有人家的情况下更新
- cmxlen[u] 和 mx[id] 顺带维护就好了
- 以上是第一次 dfs
- 为了求得 mxlen[u] 的真正含义,有好几种情况需要考虑 :
- 当 (sz[v] == 0),也就是最长链肯定不在 v 的子树下
- mxlen[v] = mxlen[u] + dis(u, v); mxid[v] = u;
- 当 (sz[v] == K),也就是最长链肯定在 v 的子树下
- 那么 mxlen[v] 就不需要更新
- 否则就是 v 的子树下有人家,并且父节点 u 的上面(也就是除了 v 子树)也有节点
- if( mxlen[u] + dis(u, v) >= mxlen[v] && mxid[u] != v ),此时更新 mxlen[v], cmxlen[v], mxid[v]
- if( mxlen[u] + dis(u, v) >= mxlen[v] && mxid[u] == v ),此时最长链路不能更新,因为 v 不作为链路的一端
- if( cmxlen[u] + dis(u, v) >= mxlen[v] ),此时次长链肯定不会经过节点 v,因为如果经过,那么在最长链中就已经更新过了,轮不到这个 if,于是更新 mxlen[v], cmxlen[v]
- if( mxlen[u] + dis(u, v) >= cmxlen[v] && mxid[u] != v ),此时更新 cmxlen[v]
- if( mxlen[u] + dis(u, v) >= cmxlen[v] && mxid[u] == v ),此时次长链路不能更新,因为 v 不作为链路的一端
- if( cmxlen[u] + dis(u, v) >= cmxlen[v] ),此时次长链肯定不会经过节点 v,因为如果经过,那么在前面更新次长链中就已经更新过了,轮不到这个 if,于是更新 cmxlen[v]
- 当 (sz[v] == 0),也就是最长链肯定不在 v 的子树下
- 以上是第二次 dfs
代码
1 |
|